/*
 * wtrp.cpp
 *
 *  Created on: Aug 10, 2012
 *      Author: adroaldo
 */

#include <my-wtr-proto/wtrp.h>

/* Debug definition*/
#define DEBUG

/*******************************/
/** Conectando TCL às classes **/
/*******************************/
int hdr_wtrp_packet::offset_;

static class WTRPHeaderClass: public PacketHeaderClass {
public:
	WTRPHeaderClass() :
		PacketHeaderClass("PacketHeader/WTRP", sizeof(hdr_all_packets)) {
		bind_offset(&hdr_wtrp_packet::offset_);
	}
} class_rtProtoWTRP_hdr;

/*************************************/
/* Bind Agent class to tcl interface */
/*************************************/

static class WtrpClass: public TclClass {
public:
	WtrpClass() :
		TclClass("Agent/WTRP") {
	}
	TclObject *create(int argc, const char* const * argv) {
		assert(argc == 5);
		return (new wtrp((nsaddr_t) Address::instance().str2addr(argv[4])));
	}
} class_rtWtrp;

wtrp::wtrp(nsaddr_t id) :
	Agent(PT_WTRP), send_packet_timer(this, &wtrp::send_wtrp_packet_timer),
			reset_packet_timer(this, &wtrp::reset_wtrp_packet_timer) {

#ifdef DEBUG
	printf("N (%.6f): Agente de roteamento inicializado para node %d\n",
			CURRENT_TIME, id);
#endif

	i_node = new node(id);

	pos_x = 0;
	pos_y = 0;
	node_energy = 0.0;

	logtarget_ = 0;
	ifqueue_ = 0;

	iNode = (MobileNode *) (Node::get_node_by_address(id));
}

WTRP_Timer::WTRP_Timer(wtrp *agent, firefunction f) :
	TimerHandler() {
	agent_ = agent;
	firing_ = f;
}

void WTRP_Timer::expire(Event *e) {
	(agent_->*firing_)();
}

void wtrp::recv_packet(Packet *p) {

	struct hdr_ip* hdrip = HDR_IP(p);
	struct hdr_wtrp_packet *wtrph = HDR_WTRP_PACKET(p);
	struct hdr_advertising *hdrA = 0;

	/*All routing messages are sent from and to port RT_PORT,*/
	/*so we check it*/

	assert(ih->sport == RT_PORT);
	assert(ih->dport == RT_PORT);

	switch (wtrph->FC) {
	case SELF_ADVERTISING:
		hdrA = HDR_ADVERTISING(p);
		if ((u_int32_t) hdrip->daddr() == IP_BROADCAST) {

			if (hdrA->ret == 0) {
				printf("Recv BRDCAST REQ : at %d.%d from %d.%d\n", here_.addr_,
						here_.port_, hdrip->saddr(), hdrip->sport());
				Packet::free(p);

				// create reply
				Packet* pktret = allocpkt();

				struct hdr_advertising* hdrret = HDR_ADVERTISING(pktret);
				struct hdr_ip* ipret = HDR_IP(pktret);

				hdrret->ret = 1;
				hdrret->SA = i_node->getNode_address();
				hdrret->FC = SELF_ADVERTISING;
				hdrret->send_time = CURRENT_TIME;

				// add brdcast address
				ipret->daddr() = IP_BROADCAST;
				ipret->dport() = ipret->sport();

				/*send(, 0);*/
				Scheduler::instance().schedule(target_, pktret, JITTER);

			} else {
				printf("Recv BRDCAST REPLY : at %d.%d from %d.%d\n",
						here_.addr_, here_.port_, hdrip->saddr(),
						hdrip->sport());
				Packet::free(p);
			}
		}
		break;
	default:
		break;
	}

	/*Release resources*/
	Packet::free(p);
}

void wtrp::send_packet() {

	Packet* p = allocpkt();
	struct hdr_cmn* ch = HDR_CMN(p);
	struct hdr_ip* ih = HDR_IP(p);
	struct hdr_advertising* ph = HDR_ADVERTISING(p);

	ph->SA = i_node->getNode_address();
	ph->FC = SELF_ADVERTISING;
	ph->send_time = CURRENT_TIME;
	ph->ret = 0;

	ch->ptype() = PT_WTRP;
	ch->direction() = hdr_cmn::DOWN;
	ch->size() = IP_HDR_LEN + ph->size();
	ch->error() = 0;
	ch->next_hop() = IP_BROADCAST;
	ch->addr_type() = NS_AF_INET;

	/*Escrevendo cabecalho IP*/
	ih->saddr() = i_node->getNode_address();
	ih->daddr() = IP_BROADCAST;
	ih->sport() = RT_PORT;
	ih->dport() = RT_PORT;
	ih->ttl_ = IP_DEF_TTL;

	Scheduler::instance().schedule(target_, p, JITTER);
}

void wtrp::send_wtrp_packet_timer() {
	printf("wtrp::send_wtrp_packet_timer Node (%.6d)\n",
			i_node->getNode_address());
}

void wtrp::reset_wtrp_packet_timer() {
	reset_packet_timer.resched((double) 5.0);
}

void wtrp::recv(Packet *p, Handler *h) {

	struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_ip *ih = HDR_IP(p);

	//ch->next_hop() = IP_BROADCAST;
	//ch->direction() = hdr_cmn::DOWN;
	//ch->addr_type() = NS_AF_INET;

	/*if (ih->saddr() == (i_node->getNode_address())) {
		printf("Recebi pacote que enviei\n");
		drop(p, DROP_RTR_ROUTE_LOOP);
	} else*/
		recv_packet(p);
}

int wtrp::command(int argc, const char * const *argv) {

	if (argc == 2) {

		if (strcasecmp(argv[1], "start") == 0) {
			return TCL_OK;
		}

		else if (strcasecmp(argv[1], "send") == 0) {

			return TCL_OK;
		}

		else if (strcasecmp(argv[1], "start-WL-bcast") == 0) {
			send_packet();
			return TCL_OK;
		}
	}// Pass the command to the base class
	else if (argc == 3) {
		/*if(strcmp(argv[1], "index") == 0) {
		 index = atoi(argv[2]);
		 return TCL_OK;
		 }*/

		if (strcmp(argv[1], "log-target") == 0
				|| strcmp(argv[1], "tracetarget") == 0) {
			logtarget_ = (Trace*) TclObject::lookup(argv[2]);
			if (logtarget_ == 0)
				return TCL_ERROR;
			return TCL_OK;
		}

		else if (strcmp(argv[1], "drop-target") == 0) {
			return TCL_OK;
		}

		else if (strcmp(argv[1], "if-queue") == 0) {
			ifqueue_ = (PriQueue*) TclObject::lookup(argv[2]);

			if (ifqueue_ == 0)
				return TCL_ERROR;
			return TCL_OK;
		}

		else if (strcmp(argv[1], "port-dmux") == 0) {
			dmux_ = (PortClassifier *) TclObject::lookup(argv[2]);
			if (dmux_ == 0) {
				fprintf(stderr, "%s: %s lookup of %s failed\n", __FILE__,
						argv[1], argv[2]);
				return TCL_ERROR;
			}
			return TCL_OK;
		}
	}
	return Agent::command(argc, argv);
}

void wtrp::update_position() {

	iNode->update_position();
	pos_x = iNode->X();
	pos_y = iNode->Y();

	printf("U (%.6f): UPDATE POSITION, for Node %d, X: %.6u and Y: %.6u\n",
			CURRENT_TIME, i_node->getNode_address(), pos_x, pos_y);
}

void wtrp::update_energy() {

	node_energy = iNode->energy_model()->energy();

	printf("U (%.6f): UPDATE ENERGY, for Node %d, Energy: %.4u\n",
			CURRENT_TIME, i_node->getNode_address(), node_energy);
}
